package com.brightcove.opensource.workflowengine.actors; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.io.StringReader; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; import com.brightcove.opensource.workflowengine.Actor; import com.brightcove.opensource.workflowengine.Workflow; import com.brightcove.opensource.workflowengine.record.Property; import com.brightcove.opensource.workflowengine.record.Record; /** * <p> * A CSV (Comma Separated Value) input adapter. * </p> * * <p> * This actor will read a CSV file from disk, and convert each line into a * Record for upstream consumers. While labelled CSV, this should be able * to parse any character-delimited file if properly escaped. * </p> * * <p> * The following configuration properties will be honored:<ul> * <li>input-file - Location of CSV file to read in</li> * <li>delimiter - Delimiter separating columns (defaults to ,)</li> * <li>quote - Quote character defining cells (defaults to ')</li> * <li>header-row - Header row to use to define column names</li> * <li>has-header-row - If true (and header-row is not specified), * column names will be pulled from first line of file. If * header-row is also specified, header-row will be used for column * names, but the first row of the file will be skipped.</li> * </ul> * </p> * * @author <a href="https://github.com/three4clavin">Sander Gates</a> * */ public class CSVInputAdapter extends Actor { /** * <p> * Default constructor - workflow this actor is part of must be * provided * </p> * * @param workflow Workflow this actor will be part of */ public CSVInputAdapter(Workflow workflow){ super(workflow); } /* (non-Javadoc) * @see com.brightcove.opensource.workflowengine.Actor#run() */ public void run(){ Property<String> inputFileProp = getFirstProperty("input-file"); if((inputFileProp == null) || (inputFileProp.getValue() == null)){ die("CSVAdapter requires an input-file property."); } File inputFile = new File(inputFileProp.getValue()); if(! inputFile.exists()){ die("[ERR] CSVAdapter input file (" + inputFile.getAbsolutePath() + ") does not exist."); } Character delimiter = ','; Character quote = '"'; String delimiterProp = getFirstPropertyValue("delimiter"); if(delimiterProp != null){ delimiter = delimiterProp.charAt(0); } String quoteProp = getFirstPropertyValue("quote"); if(quoteProp != null){ quote = quoteProp.charAt(0); } CSVRecord headerRow = null; String headerRowProp = getFirstPropertyValue("header-row"); if(headerRowProp != null){ Reader reader = new StringReader(headerRowProp);; Iterable<CSVRecord> parser = null; try { parser = CSVFormat.newBuilder().withDelimiter(delimiter).withQuoteChar(quote).parse(reader); } catch (Exception e) { die("[ERR] CSVAdapter can't parse header row (" + headerRowProp + "). Exception caught: '" + e + "'."); } for(CSVRecord csvRecord : parser){ if(headerRow == null){ headerRow = csvRecord; } } } Boolean hasHeaderRow = false; String hasHeaderRowProp = getFirstPropertyValue("has-header-row"); if("true".equalsIgnoreCase(hasHeaderRowProp)){ hasHeaderRow = true; } if(! hasStarted()){ FileReader reader = null; Iterable<CSVRecord> parser = null; try { reader = new FileReader(inputFile); parser = CSVFormat.newBuilder().withDelimiter(delimiter).withQuoteChar(quote).parse(reader); } catch (Exception e) { die("[ERR] CSVAdapter can't parse input file (" + inputFile.getAbsolutePath() + "). Exception caught: '" + e + "'."); } Integer rowIdx = 0; for (CSVRecord csvRecord : parser) { rowIdx++; if(hasHeaderRow && (rowIdx == 1)){ if(headerRow == null){ // Only use header row from file if it wasn't supplied by property headerRow = csvRecord; } } else{ Record record = new Record(); for(Integer idx=0; idx<csvRecord.size(); idx++){ String propName = "Column-"+idx; if((headerRow != null) && (idx < headerRow.size())){ propName = headerRow.get(idx); } Property<?> prop = new Property<String>(propName, csvRecord.get(idx)); record.addProperty(prop); } handleRecord(record); } } } } }